package com.sunlands.zlcx.usercenter.exception;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sunlands.zlcx.usercenter.common.HttpHelper;
import com.sunlands.zlcx.usercenter.constant.ResultStatus;
import com.sunlands.zlcx.usercenter.service.ApiRequestStatisticsServiceImpl;
import com.sunlands.zlcx.usercenter.util.DateUtil;
import com.sunlands.zlcx.usercenter.util.Version;
import com.sunlands.zlcx.usercenter.vo.CommonResultMessage;
import com.sunlands.zlcx.usercenter.vo.response.BusinessResult;
import com.sunlands.zlcx.usercenter.vo.response.ResultVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutionException;

import static com.sunlands.zlcx.usercenter.vo.CommonResultMessage.*;

/**
 * <p>全局异常处理</p>
 * <PRE>
 * <BR>    修改记录
 * <BR>-----------------------------------------------
 * <BR>    修改日期         修改人          修改内容
 * </PRE>
 *
 * @author zl
 * @version 1.0
 * @date Created in 2019/3/15 12:00
 * @copyright: Copyright (c) founders
 */
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

    @Autowired
    private SendExceptionUtil sendExceptionUtil;
    @Autowired
    private ApiRequestStatisticsServiceImpl apiRequestStatisticsService;
    @Resource(name = "commonTaskExecutor")
    private AsyncTaskExecutor commonTaskExecutor;

    /**
     * 频次限制异常处理
     *
     * @param exception
     * @return
     */
    @ExceptionHandler({RequestLimitException.class})
    public ResponseEntity<Object> handleLimitException(HttpServletRequest request, RequestLimitException exception) {
        log.error("【handleLimitException】：" + exception.getMessage());
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        ResultVO resultVO = new ResultVO();
        resultVO.setState(ResultStatus.FAILED.getCode());
        resultVO.setMessage(exception.getMessage());
        resultVO.setAlertMessage(exception.getMessage());

        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(resultVO);
    }


    @ExceptionHandler({UserNotLearningException.class})
    public ResponseEntity<Object> handleUserNotLearningException(UserNotLearningException exception, HttpServletRequest request) {
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        BusinessResult<Object> businessResult = BusinessResult.createInstance(PARAMS_NOT_INVALIDE.getCode(), exception.getMessage());
        String v = Version.getVersion(request);
        //通过version判断是否为客户端请求，如果是客户端请求返回200错误码，如果不是返回400，
        //目的是为了兼容老版本
        log.warn("UserNotLearningException {} v = {} ", exception.getMessage(), v);
        return ResponseEntity.status(Objects.isNull(v) ? HttpStatus.BAD_REQUEST : HttpStatus.OK).body(businessResult);
    }


    @ExceptionHandler({UserTimeLearningException.class})
    public ResponseEntity<Object> handleUserLearningException(UserTimeLearningException exception, HttpServletRequest request) {
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        BusinessResult<Object> businessResult = BusinessResult.createInstance(USER_TIME_LEARNING.getCode(), exception.getMessage());
        String v = Version.getVersion(request);
        //通过version判断是否为客户端请求，如果是客户端请求返回200错误码，如果不是返回400，
        //目的是为了兼容老版本
        log.warn("UserTimeLearningException {} v = {} ", exception.getMessage(), v);
        return ResponseEntity.status(Objects.isNull(v) ? HttpStatus.BAD_REQUEST : HttpStatus.OK).body(businessResult);
    }


    @ExceptionHandler({UserLiveLearningException.class})
    public ResponseEntity<Object> handleUserLiveLearningException(UserLiveLearningException exception, HttpServletRequest request) {
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        BusinessResult<Object> businessResult = BusinessResult.createInstance(USER_LIVE_LEARNING.getCode(), exception.getMessage());
        //通过version判断是否为客户端请求，如果是客户端请求返回200错误码，如果不是返回400，
        //目的是为了兼容老版本
        String v = Version.getVersion(request);
        log.warn("UserLiveLearningException {} v = {} ", exception.getMessage(), v);
        return ResponseEntity.status(Objects.isNull(v) ? HttpStatus.BAD_REQUEST : HttpStatus.OK).body(businessResult);
    }

    @ExceptionHandler({UserTimeRoomLearningException.class})
    public ResponseEntity<Object> handleUserTimeRoomLearningException(UserTimeRoomLearningException exception, HttpServletRequest request) {
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        BusinessResult<Object> businessResult = BusinessResult.createInstance(TIME_LEARN_ROOM_LEARNING.getCode(), exception.getMessage());
        String v = Version.getVersion(request);
        log.warn("UserTimeRoomLearningException {} v = {} ", exception.getMessage(), v);
        return ResponseEntity.status(Objects.isNull(v) ? HttpStatus.BAD_REQUEST : HttpStatus.OK).body(businessResult);
    }

    /**
     * 参数异常处理
     *
     * @param e e
     * @return
     */
    @ExceptionHandler({CheckParamException.class})
    public ResponseEntity<BusinessResult<Object>> handleParamException(CheckParamException e, HttpServletRequest request) {
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String paramInfo = "";
        paramInfo = getParam(request, method, paramInfo);
        log.error("API: BusinessException={} cause={} method={} uri={} paramInfo={}", e.getClass().getSimpleName(), e.getMessage(), method, uri, paramInfo);
        log.error("", e);
        BusinessResult<Object> businessResult = BusinessResult.createInstance(PARAMS_NOT_INVALIDE.getCode(), e.getMessage());
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(businessResult);
    }

    /**
     * 参数异常处理
     *
     * @param e
     * @return
     */
    @ExceptionHandler({IllegalArgumentException.class})
    public ResponseEntity<BusinessResult<Object>> handleIllegalArgumentException(IllegalArgumentException e, HttpServletRequest request) {
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String paramInfo = "";
        paramInfo = getParam(request, method, paramInfo);
        log.error("API: IllegalArgumentException={} cause={} method={} uri={} paramInfo={}", e.getClass().getSimpleName(), e.getMessage(), method, uri, paramInfo);
        log.error("", e);
        BusinessResult<Object> businessResult = BusinessResult.createInstance(PARAMS_NOT_INVALIDE.getCode(), e.getMessage());
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(businessResult);
    }


    /**
     * 参数异常处理
     *
     * @param e
     * @return
     */
    @ExceptionHandler({IllegalStateException.class})
    public ResponseEntity<BusinessResult<Object>> handleIllegalStateException(IllegalStateException e, HttpServletRequest request) {
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String paramInfo = "";
        paramInfo = getParam(request, method, paramInfo);
        log.error("API: BusinessException={} cause={} method={} uri={} paramInfo={}", e.getClass().getSimpleName(), e.getMessage(), method, uri, paramInfo);
        log.error("", e);
        BusinessResult<Object> businessResult = BusinessResult.createInstance(PARAMS_NOT_INVALIDE.getCode(), e.getMessage());
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(businessResult);
    }

    /**
     * 业务异常
     *
     * @param e e
     * @return
     */
    @ExceptionHandler({BusinessException.class})
    public ResponseEntity<BusinessResult<Object>> handleBusinessException(BusinessException e, HttpServletRequest request) {
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String paramInfo = "";
        paramInfo = getParam(request, method, paramInfo);
        log.error("API: BusinessException={} cause={} method={} uri={} paramInfo={}", e.getClass().getSimpleName(), e.getMessage(), method, uri, paramInfo);
        log.error("", e);
        formatException(e, method, uri, paramInfo);
        BusinessResult<Object> businessResult = BusinessResult.createInstance(UNKOWN_ERROR.getCode(), e.getMessage());
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(businessResult);

    }

    private void formatException(Exception e, String method, String uri, String paramInfo) {
        sendExceptionUtil.sendException("\n4. [" + method + "]：" + uri + "\n5. [paramInfo]：" + paramInfo + "\n6. [datetime]：" + DateUtil.getStringDate(), e);
    }

    /**
     * 限制登陆异常
     *
     * @param e e
     * @return
     */
    @ExceptionHandler({UserLoginLimitException.class})
    public ResponseEntity<BusinessResult<Object>> handleUserLoginLimitException(UserLoginLimitException e, HttpServletRequest request) {
//        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String paramInfo = "";
        paramInfo = getParam(request, method, paramInfo);
        log.error("API: BusinessException={} cause={} method={} uri={} paramInfo={}", e.getClass().getSimpleName(), e.getMessage(), method, uri, paramInfo);
        log.error("", e);
        BusinessResult<Object> businessResult = BusinessResult.createInstance(LOGIN_LIMIT.getCode(), LOGIN_LIMIT.message);
        return ResponseEntity.status(HttpStatus.OK).body(businessResult);
    }

    @ExceptionHandler({ExecutionException.class})
    public ResponseEntity<BusinessResult<Object>> handleExecutionException(ExecutionException e, HttpServletRequest request) {
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String paramInfo = "";
        paramInfo = getParam(request, method, paramInfo);
        log.error("API: 触发用户状态错误 ExecutionException={} cause={} method={} uri={} paramInfo={}", e.getClass().getSimpleName(), e.getMessage(), method, uri, paramInfo);
        log.error("", e);
        formatException(e, method, uri, paramInfo);
        BusinessResult<Object> businessResult = BusinessResult.createInstance(UNKOWN_ERROR.getCode(), e.getMessage());
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(businessResult);
    }


    /**
     * 参数异常
     */
    @ExceptionHandler({IllegalAccessException.class})
    public ResponseEntity<BusinessResult<Object>> handleIllegalAccessException(IllegalAccessException exception, HttpServletRequest request) {
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        log.error("【handleIllegalIllegalAccessException】：" + exception.getMessage());
        BusinessResult<Object> businessResult = BusinessResult.createInstance(PARAMS_NOT_INVALIDE.getCode(), exception.getMessage());
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(businessResult);
    }

    /**
     * 权限异常处理
     *
     * @param exception exception
     * @return ResponseEntity
     */
    @ExceptionHandler({MethodArgumentTypeMismatchException.class})
    public ResponseEntity<BusinessResult<Object>> handleAccessDeniedException(MethodArgumentTypeMismatchException exception, HttpServletRequest request) {
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        log.error("【handleException】：" + exception.getMessage());
        log.error("", exception);
        BusinessResult<Object> businessResult = BusinessResult.createInstance(PARAMS_NOT_INVALIDE.code, exception.getMessage());
        return ResponseEntity.status(HttpStatus.OK).body(businessResult);
    }

    /**
     * 异常处理
     *
     * @param exception exception
     * @return ResponseEntity
     */
    @ExceptionHandler({CheckTargetException.class})
    public ResponseEntity<BusinessResult<Object>> handleCheckException(CheckTargetException exception, HttpServletRequest request) {
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        log.error("【handleCheckException】：" + exception.getMessage());
        log.error("", exception);
        BusinessResult<Object> businessResult = BusinessResult.createInstance(DEL_TARGET_ERROR.code, exception.getMessage());
        return ResponseEntity.status(HttpStatus.OK).body(businessResult);
    }

    /**
     * 权限异常处理
     *
     * @param exception exception
     * @return ResponseEntity
     */
    @ExceptionHandler({AccessDeniedException.class})
    public ResponseEntity<BusinessResult<Object>> handleAccessDeniedException(AccessDeniedException exception, HttpServletRequest request) {
//        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        log.error("【handleAccessDeniedException】：" + exception.getMessage());
        BusinessResult<Object> businessResult = BusinessResult.createInstance(NO_AUTH.code, "请传入token，" + exception.getMessage());
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(businessResult);
    }

    /**
     * 业务异常处理
     *
     * @param e
     * @return
     */
    @ExceptionHandler({Exception.class})
    public ResponseEntity<BusinessResult<Object>> handleException(Exception e, HttpServletRequest request) {
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String paramInfo = "";
        paramInfo = getParam(request, method, paramInfo);
        log.error("API: excepiton={} cause={} method={} uri={} paramInfo={}", e.getClass().getSimpleName(), e.getMessage(), method, uri, paramInfo);
        log.error("", e);
        formatException(e, method, uri, paramInfo);
        BusinessResult<Object> businessResult = BusinessResult.createInstance(CommonResultMessage.UNKOWN_ERROR.getCode(), e.getMessage());
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(businessResult);
    }

    private String getParam(HttpServletRequest request, String method, String paramInfo) {
        if (RequestMethod.GET.name().equals(method)) {
            paramInfo = request.getQueryString();
        } else if ((RequestMethod.POST.name().equals(method) || RequestMethod.PUT.name().equals(method))) {
            String contentType = request.getHeader("Content-Type");
            if (contentType != null && !contentType.contains("multipart")) {
                Map<String, String[]> params = request.getParameterMap();
                if (params != null && params.size() == 0) {
                    paramInfo = HttpHelper.getBodyString(request);
                } else {
                    Map<String, Object> paramsObj = new HashMap<>();
                    for (String key : params.keySet()) {
                        paramsObj.put(key, params.get(key));
                    }
                    try {
                        paramInfo = objectMapper.writeValueAsString(paramsObj);
                    } catch (JsonProcessingException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return paramInfo;
    }

    @Autowired
    private ObjectMapper objectMapper;


    /**
     * 平台基类异常处理
     */
    @ExceptionHandler({PlatformException.class})
    public ResponseEntity<BusinessResult<Object>> handlePlatformException(PlatformException e, HttpServletRequest request) {
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String paramInfo = "";
        paramInfo = getParam(request, method, paramInfo);
        log.error("API: excepiton={} cause={} method={} uri={} paramInfo={}", e.getClass().getSimpleName(), e.getMessage(), method, uri, paramInfo);
        log.error("", e);
        formatException(e, method, uri, paramInfo);
        BusinessResult<Object> businessResult = BusinessResult.createInstance(CommonResultMessage.UNKOWN_ERROR.getCode(), e.getMessage());
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(businessResult);
    }


}
